<?php

namespace Yolo\Components\Middleware;

use ReflectionException;
use Yolo\Di\Annotations\Singleton;
use Yolo\Di\DI;
use Yolo\Di\Errors\CircularDependencyException;
use Yolo\Di\Errors\InvalidAttributeException;
use Yolo\Di\Errors\ParameterTypeEmptyException;
use Yolo\Service\RpcRequest;
use Yolo\Service\RpcResponse;
use Yolo\Utils\Console;

/**
 * Middleware manager
 */
#[Singleton]
class MiddlewareManager
{

    /**
     * @var MiddlewareInterface[] $middlewares Middleware instances.
     */
    private array $middlewares = [];

    private array $sort = [];

    /**
     * Register a middleware.
     * @param string $middleware
     * @return void
     * @throws ReflectionException
     * @throws CircularDependencyException
     * @throws InvalidAttributeException
     * @throws ParameterTypeEmptyException
     */
    public function register(string $middleware): void
    {
        if (isset($this->middlewares[$middleware])) {
            return;
        }

        $instance = DI::use($middleware, false);

        if (!$instance instanceof MiddlewareInterface) {
            Console::log("Middleware $middleware is not implement MiddlewareInterface, ignored.", 'warning');
            return;
        }

        $this->sort[$middleware] = $instance->priority();
        arsort($this->sort);
        $this->middlewares[$middleware] = $instance;

        Console::log("Middleware: Middleware $middleware registered.");
    }

    /**
     * Run before request handled.
     * @param $request
     * @param $response
     * @return array{0: RpcRequest, 1: RpcResponse}
     */
    public function runBeforeRequestHandle($request, $response): array
    {
        $middlewares = array_filter(array_keys($this->sort), function ($m) {
            return $m !== 'AfterRequestHandledMiddleware';
        });

        // run middlewares
        foreach ($middlewares as $middlewareName) {

            $middleware = $this->middlewares[$middlewareName];
            if ($middleware->filter($request)) {
                continue;
            }

            [$request, $response] = $middleware->handle(
                $request,
                $response,
                function ($request, $response) {
                    return [$request, $response];
                }
            );
        }

        return [$request, $response];
    }

    /**
     * Run after request handled.
     * @param $request
     * @param $response
     * @return array{0: RpcRequest, 1: RpcResponse}
     */
    public function runAfterRequestHandled($request, $response): array
    {
        $middlewares = array_filter(array_keys($this->sort), function ($m) {
            return $m === 'AfterRequestHandledMiddleware';
        });

        // run middlewares
        foreach ($middlewares as $middlewareName) {

            $middleware = $this->middlewares[$middlewareName];
            if ($middleware->filter($request)) {
                continue;
            }

            [$request, $response] = $middleware->handle(
                $request,
                $response,
                function ($request, $response) {
                    return [$request, $response];
                }
            );
        }

        return [$request, $response];
    }
}
